// service
import axios, { AxiosResponse } from 'axios'
import type { AxiosInstance, InternalAxiosRequestConfig } from 'axios'
import type {
  RequestConfig,
  RequestInterceptors,
  CancelRequestSource
} from './types'

class MyRequest {
  // axios 实例
  instance: AxiosInstance
  // 存放实例拦截器对象
  interceptorsConfig?: RequestInterceptors
  /*
  存放取消方法的集合
  * 在创建请求后将取消请求方法 push 到该集合中
  * 封装一个方法，可以取消请求，传入 url: string|string[]
  * 在请求之前判断同一URL是否存在，如果存在就取消请求
  */
  // cancelRequestSourceList?: CancelRequestSource[]
  /*
  存放所有请求URL的集合
  * 请求之前需要将url push到该集合中
  * 请求完毕后将url从集合中删除
  * 添加在发送请求之前完成，删除在响应之后删除
  */
  // requestUrlList?: string[]

  constructor(config: RequestConfig) {
    this.instance = axios.create(config)
    this.interceptorsConfig = config.interceptors

    this.instance.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        return config
      },
      (err: any) => err
    )

    // 使用实例拦截器
    this.instance.interceptors.request.use(
      this.interceptorsConfig?.requestInterceptors,
      this.interceptorsConfig?.requestInterceptorsCatch
    )
    this.instance.interceptors.response.use(
      this.interceptorsConfig?.responseInterceptors,
      this.interceptorsConfig?.responseInterceptorsCatch
    )

    // 全局响应拦截器保证最后执行
    this.instance.interceptors.response.use(
      (response: AxiosResponse) => {
        return response
      },
      (err: any) => err
    )
  }

  // 取消请求 --- > 预留
  static cancelRequestSourceList: CancelRequestSource[] = []
  static requestUrlList: string[] = []

  /**
   * @description: 获取指定 url 在 cancelRequestSourceList 中的索引
   * @param {string} url
   * @returns {number} 索引位置
   */
  static getSourceIndex(url: string): number {
    return MyRequest.cancelRequestSourceList?.findIndex(
      (item: CancelRequestSource) => {
        return Object.keys(item)[0] === url
      }
    ) as number
  }
  /**
   * @description: 删除 requestUrlList 和 cancelRequestSourceList
   * @param {string} url
   * @returns {*}
   */
  static delUrl(url: string) {
    const urlIndex = MyRequest.requestUrlList?.findIndex((u) => u === url)
    const sourceIndex = MyRequest.getSourceIndex(url)
    // 删除url和cancel方法
    urlIndex !== -1 && MyRequest.requestUrlList?.splice(urlIndex as number, 1)
    sourceIndex !== -1 &&
      MyRequest.cancelRequestSourceList?.splice(sourceIndex as number, 1)
  }

  // 取消全部请求
  static cancelAllRequest() {
    MyRequest.cancelRequestSourceList?.forEach((source) => {
      const key = Object.keys(source)[0]
      source[key]()
    })
  }

  // 取消请求
  static cancelRequest(url: string | string[]) {
    if (typeof url === 'string') {
      // 取消单个请求
      const sourceIndex = MyRequest.getSourceIndex(url)
      sourceIndex >= 0 &&
        MyRequest.cancelRequestSourceList?.[sourceIndex][url]()
    } else {
      // 存在多个需要取消请求的地址
      url.forEach((u) => {
        const sourceIndex = MyRequest.getSourceIndex(u)
        sourceIndex >= 0 &&
          MyRequest.cancelRequestSourceList?.[sourceIndex][u]()
      })
    }
  }

  // extraConfig: 额外参数 --- > 预留
  request<T = any>(config: RequestConfig<T>, extraConfig?: any): Promise<T> {
    return new Promise((resolve, reject) => {
      // // 如果我们为单个请求设置拦截器，这里使用单个请求的拦截器
      if (config.interceptors?.requestInterceptors) {
        config = config.interceptors.requestInterceptors({
          ...config
        } as InternalAxiosRequestConfig)
      }

      // 设置 取消请求队列  预留
      // const url = config.url
      // if (url) {
      //   MyRequest.requestUrlList?.push(url)
      //   config.cancelToken = new axios.CancelToken((c) => {
      //     MyRequest.cancelRequestSourceList?.push({
      //       [url]: c
      //     })
      //   })
      // }

      this.instance
        .request<T>(config)
        .then((res: AxiosResponse) => {
          // 如果我们为单个响应设置拦截器，这里使用单个响应的拦截器
          if (config.interceptors?.responseInterceptors) {
            res = config.interceptors.responseInterceptors(res)
          }
          resolve(res.data)
        })
        .catch((err) => {
          // 如果我们为单个响应设置拦截器，这里使用单个响应的拦截器
          if (config.interceptors?.responseInterceptorsCatch) {
            err = config.interceptors.responseInterceptorsCatch(err)
          }
          reject(err.response?.data)
        })
        .finally(() => {
          // url && MyRequest.delUrl(url)
        })
    })
  }
}

export default MyRequest
